/**
 * @author temsa
 * Describes the Standard Component Interface
 * @class Archetype.component.graphicalComponent
 * @name Archetype.component.graphicalComponent
 * @extends Archetype.component.abstractComponent
 */
Archetype.Component.create(
/**
 * @scope Archetype.component.graphicalComponent.prototype
 */
{
    name:"Archetype.component.graphicalComponent",
    setup: {
        dependencies:{components:{}, lib:[], css:[], html:{}}
    },
    /**
     * @function
     * Assume by convention default view name
     */
    beforeInstanciate: function() {
    	if(typeof this.setup == "undefined") {
        	this.setup = {};
        }
    	if(typeof this.setup.dependencies == "undefined") {
        	this.setup.dependencies = {};
        }
        if (typeof this.setup.dependencies.html == "undefined") {
        	//if no template is defined for a graphical component, we assume a default one
        	this.setup.dependencies.html = {
        		//the two "(.*)" in the regexp are used to match the last match of component
    			main: this.name.replace(/(.*)component(.*)/, "$1template$2")
    		};
    	}
        this.anchor = this.generateUid();
    },
    /**
     * @function
     * Default empty constructor
     */
    initialize : _,
    /**
     * Array of parent_Elements where the component is
     */
    parent_Elements: [],
    /**
     * Generates a unique Id for the current page and stores it in this.uid,
     * so you can add a unique id in your template, for example to store your anchor
     */
    generateUid: function() {
        var date = new Date();
        this.uid = this.name + "-" + date.getTime() + date.getMilliseconds();
        return this.uid;
    },
    /**
     * Generic elements for graphical uses
     */
    addUnderElement: function(parentElement) {
        try {
            var args = $A(arguments);
            args = args.without(args.first());
            this.parent_Elements.push(parentElement);
            Logger.warn(parentElement, this.parent_Elements, $H(this.render).inspect(),this.render(args));
            new Insertion.Bottom(parentElement, this.render(args));
        } catch(e) {
            Logger.error(e);
        }
    },
	/**
	 * Renders the view as a String, must be implemented by the graphical component
	 * Takes no parameters.
	 *
	 * @return {String} the view as a string to insert in the DOM
	 */
	renderViewAsString:  function(template) {
            template = template || "main";
            if( $exist(this.templates) && $exist(this.templates[template]) ) {
            	return this.templates[template].evaluate(this);
            } else {
                Logger.warn("Please override 'renderViewAsString' or define a '"+template+"' template.");
            }
            return "";
    	},
        write: function (template, anchor ) {

        },
	/**
	 * Render the view. Uses renderViewAsString().
	 *
	 * @param {DOMNode} Element on which the Insertion will be applied
	 * @param {Insertion} [Insertion object] Usually prototype ones. Do not define to replace the element.
	 * @see http://www.prototypejs.org/api/insertion
	 *
	 * @exemple this.render(document.body, Insertion.Bottom);
	 */
	render: function(/*DOMNode*/element, /*Insertion*/insertionObject) {
    	if(typeof element == "undefined" && typeof this.anchor != "undefined") {
    		element = this.anchor;
    	}
    		
	    if(typeof insertionObject == "undefined") {
	    	$(element).update(this.renderViewAsString());
	    } else {
	    	new insertionObject(element, this.renderViewAsString());
	    }
	    
	    this.afterRender();
	    this._instanciateInnerComponents(element);
	},
	/**
	 * Called by default after a "render"<br>
	 * override this to set handlers after a typical render<br>
	 * By default, apply behaviors listeners 
	 * @function
	 */
	afterRender: function() {
		var that = this;
		
		if(this.anchor != null) {
			var anchor = $E(this.anchor);
		} else {
			var anchor = $E(document);
		}
		
		if(this.observe != null) {
			var observe = this.observe;
		} else {
			var observe = function(element, eventType, callback) {
				$E(element).observe(eventType, callback);
			}
		}
		
		if($H(this.setup.dependencies.html).size() == 1 && typeof this.setup.dependencies.html.main != "undefined") {
			$H(this.setup.behaviors).each(function(behavior) {
				var selector = behavior.key;
				var callbacks = behavior.value;
				
				anchor.select(selector).each(function(element) {
					$H(callbacks).each(function(callback) {
						var eventType = callback.key;
						var eventCallback = callback.value;
						
						observe(element, eventType, eventCallback.bind(that));
					});
				});
			});
		}
	},
	/**
	 * @param Element element
	 */
	_instanciateInnerComponents: function(/*Element*/ anchor) {
		$E(anchor).select("*").each(function(element) {
			var anchorForComponent = $E(element);
			var innerComponentName = anchorForComponent.readAttribute("archetype:component");
			if(typeof innerComponentName != "undefined" && innerComponentName != null && innerComponentName != "null") {
				anchorForComponent.removeAttribute("archetype:component");
				anchorForComponent.writeAttribute("archetype:componentInstanciated", innerComponentName);
				var component = new this.components[innerComponentName](anchorForComponent);
			}
		}.bind(this));
	}
});